package com.onpositive.gamemaker.spriteeditor;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.nebula.jface.galleryviewer.GalleryTreeViewer;
import org.eclipse.nebula.jface.galleryviewer.GalleryViewerRow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.onpositive.gamemaker.editor.dialogs.SpriteEditDialog2;
import com.onpositive.gamemaker.editor.io.ConfigPersistanceException;
import com.onpositive.gamemaker.editor.io.XMLSpritePersistance;

public class SpriteEditor extends EditorPart
{
	protected boolean dirty = false;
	
	protected Map<String, SpriteCategory> descriptors;
	protected File editedFile;
	protected SpriteDescriptor currentDescriptor;
	protected String currentCategory;
	private EditorImageManager editorImageManager;
	private ImageScrollingControl imageControl;
	
	private GalleryTreeViewer galleryViewer;

	private Button addSpriteButton;
	private Button deleteSpriteButton;
	private Button editSpriteButton;
	private Button addCategoryButton;
	private Button editCategoryButton;
	
	private SelectionListener addSelectionListener = new SelectionListener()
	{
		@Override
		public void widgetSelected(SelectionEvent e)
		{
			SpriteEditDialog2 dialog = new SpriteEditDialog2(getSite().getShell(), descriptors.keySet().toArray(new String[0]));
			dialog.setFilterPath(editedFile.getParent());
			dialog.setInitialFileName(getFileName(currentDescriptor.getId()));
			dialog.setCategory(currentCategory);
			if (dialog.open() == Dialog.OK)
			{
				SpriteDescriptor newDescriptor = dialog.getDescriptor();
				String category = dialog.getCategory().toLowerCase().trim();
				SpriteCategory spriteCategory = descriptors.get(category);
				if (spriteCategory == null)
				{
					spriteCategory = new SpriteCategory(category);
					descriptors.put(category, spriteCategory);
				}
				spriteCategory.addSprite(newDescriptor);
				galleryViewer.refresh();
				dirty = true;
				firePropertyChange(EditorPart.PROP_DIRTY);
			}
			
		}
		
		@Override
		public void widgetDefaultSelected(SelectionEvent e)
		{
			// Do nothing
		}
	};
	
	private SelectionListener editSelectionListener = new SelectionListener()
	{
		@Override
		public void widgetSelected(SelectionEvent e)
		{
			if (currentDescriptor == null)
				return;
			SpriteEditDialog2 dialog = new SpriteEditDialog2(getSite().getShell(), descriptors.keySet().toArray(new String[0]));
			dialog.setFilterPath(editedFile.getParent());
			dialog.setInitialFileName(getFileName(currentDescriptor.getId()));
			dialog.setDescriptor(currentDescriptor);
			dialog.setCategory(currentCategory);
			if (dialog.open() == Dialog.OK)
			{
				String category = dialog.getCategory().toLowerCase().trim();
				if (!category.equalsIgnoreCase(currentCategory))
				{
					SpriteCategory spriteCategory = descriptors.get(category);
					if (spriteCategory == null)
					{
						spriteCategory = new SpriteCategory(category);
						descriptors.put(category, spriteCategory);
					}
					descriptors.get(currentCategory).removeSprite(currentDescriptor);
					spriteCategory.addSprite(currentDescriptor);
					galleryViewer.refresh();
					dirty = true;
					firePropertyChange(EditorPart.PROP_DIRTY);
				}
			}
		}
		
		
		@Override
		public void widgetDefaultSelected(SelectionEvent e)
		{
			// Do nothing
		}
	};
	
	private SelectionListener deleteSelectionListener = new SelectionListener()
	{

		@Override
		public void widgetSelected(SelectionEvent e)
		{
			if (currentDescriptor == null)
				return;
			if (MessageDialog.openQuestion(getSite().getShell(),"Delete selected sprite","Are you sure you want to delete selected sprite?"))
			{
				descriptors.get(currentCategory).removeSprite(currentDescriptor);
				galleryViewer.refresh();
			}
		}

		@Override
		public void widgetDefaultSelected(SelectionEvent e)
		{
			// Do nothing
		}
		
	};
	
	@Override
	public void doSave(IProgressMonitor monitor)
	{
		try
		{
			FileOutputStream outputStream = new FileOutputStream(editedFile);
			XMLSpritePersistance.saveDescriptorList(outputStream,descriptors);
		} catch (FileNotFoundException e)
		{
			String message = "File " + editedFile.getName() + " can't be found";
			ErrorDialog.openError(getSite().getShell(),"Error while saving", message,new Status(IStatus.ERROR,"",message,e));
		} catch (ConfigPersistanceException e)
		{
			String message = "Error saving config"; 
			ErrorDialog.openError(getSite().getShell(),"Error while saving", message,new Status(IStatus.ERROR,"",message,e));
		}
	}

	@Override
	public void doSaveAs()
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void init(IEditorSite site, IEditorInput input) throws PartInitException
	{
		setSite(site);
		setInput(input);
		if (input instanceof FileStoreEditorInput)
		{
			InputStream stream;
			try
			{
				URI uri = ((FileStoreEditorInput) input).getURI();
				URL url = uri.toURL();
				stream = url.openStream();
				editedFile = new File(uri);
				descriptors = XMLSpritePersistance.loadDescriptorList(stream);
			} catch (MalformedURLException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ConfigPersistanceException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (input instanceof FileEditorInput)
		{
			try
			{
				InputStream stream = ((FileEditorInput) input).getStorage().getContents();
				editedFile = ((FileEditorInput) input).getFile().getLocation().toFile();
				descriptors = XMLSpritePersistance.loadDescriptorList(stream);
			} catch (CoreException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ConfigPersistanceException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	@Override
	public boolean isDirty()
	{
		return dirty;
	}

	@Override
	public boolean isSaveAsAllowed()
	{
		return true;
	}

	@Override
	public void createPartControl(Composite parent)
	{
		final Composite composite = new Composite(parent, SWT.NONE);
		composite.setLayout(new FormLayout());

		// Create the sash first, so the other controls
		// can be attached to it.
		Sash sash = new Sash(composite, SWT.VERTICAL);
		FormData data = new FormData();
		data.top = new FormAttachment(0, 0); // Attach to top
		data.bottom = new FormAttachment(100, 0); // Attach to bottom
		data.left = new FormAttachment(50, 0); // Attach halfway across
		sash.setLayoutData(data);

		Composite leftComposite = new Composite(composite, SWT.NONE);
	    GridLayout gridLayout = new GridLayout(1, false);
	    leftComposite.setLayout(gridLayout);
	    
	    data = new FormData();
		data.top = new FormAttachment(0, 0);
		data.bottom = new FormAttachment(100, 0);
		data.left = new FormAttachment(0, 0);
		data.right = new FormAttachment(sash, 0);
		data.width = 300;
		leftComposite.setLayoutData(data);
	    
		imageControl = new ImageScrollingControl(leftComposite, SWT.BORDER);
		imageControl.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
		
		
		createButtonBar(leftComposite);

		galleryViewer = new GalleryTreeViewer(composite, SWT.BORDER | SWT.V_SCROLL);
		data = new FormData();
		data.top = new FormAttachment(0, 0);
		data.bottom = new FormAttachment(100, 0);
		data.left = new FormAttachment(sash, 0);
		data.right = new FormAttachment(100, 0);
		galleryViewer.getControl().setLayoutData(data);
		galleryViewer.setContentProvider(new SpriteContentProvider(descriptors));
		editorImageManager = new EditorImageManager(editedFile.getParent());
		galleryViewer.setLabelProvider(new SpriteLabelProvider(editorImageManager));
		galleryViewer.setInput(descriptors);
		galleryViewer.addSelectionChangedListener(new ISelectionChangedListener()
		{
			
			@Override
			public void selectionChanged(SelectionChangedEvent event)
			{
				ISelection selection = event.getSelection();
				if (selection instanceof IStructuredSelection && !selection.isEmpty())
				{
					Object firstElement = ((IStructuredSelection) selection).getFirstElement();
					if (firstElement instanceof SpriteDescriptor)
					{
						SpriteDescriptor descriptor = (SpriteDescriptor) firstElement;
						currentDescriptor = descriptor;
						String id = descriptor.getId();						
						Image fullImage = editorImageManager.getFullImage(id);
						imageControl.setImage(fullImage);
						imageControl.setFrameBounds(descriptor.getBounds());
						composite.layout(true,true);
					}
					if (selection instanceof TreeSelection)
					{
						Object firstSegment = ((TreeSelection) selection).getPaths()[0].getFirstSegment();
						if (firstSegment instanceof SpriteCategory)
							currentCategory = ((SpriteCategory) firstSegment).getCategoryName();
					}
				}
				
			}
		});
	}

	protected String getFileName(String id)
	{
		int indexOf = id.indexOf('[');
		if (indexOf > 0)
			return id.substring(0,indexOf); 
		return id;
	}

	private void createButtonBar(Composite composite)
	{
		Composite buttonComposite = new Composite(composite, SWT.NONE); 
		buttonComposite.setLayoutData(new GridData(SWT.CENTER,SWT.BOTTOM,true, false));
		buttonComposite.setLayout(new GridLayout(5, false));
		addSpriteButton = createButton(buttonComposite, "Add", addSelectionListener);
		editSpriteButton = createButton(buttonComposite, "Edit", editSelectionListener);
		deleteSpriteButton = createButton(buttonComposite, "Delete", deleteSelectionListener);
		addCategoryButton = createButton(buttonComposite, "Add category", null);
		editCategoryButton = createButton(buttonComposite, "Edit category", null);
	}

	private Button createButton(Composite composite, String text, SelectionListener listener)
	{
		Button button = new Button(composite, SWT.PUSH);
		button.setText(text);
		if (listener != null)
		button.addSelectionListener(listener);
		return button;
	}

	@Override
	public void setFocus()
	{
		// TODO Auto-generated method stub

	}

}
